package org.KeyCrafter.key;

import org.KeyCrafter.cmd.ZSetCommand;
import org.KeyCrafter.obj.KeyCrafterObjFactory;
import org.KeyCrafter.obj.KeyCrafterObject;
import org.KeyCrafter.obj.ZSetObj;
import org.KeyCrafter.obj.type.ZSetType;
import org.KeyCrafter.server.session.ClientSession;
import org.KeyCrafter.util.SharedReply;

import java.util.ArrayList;
import java.util.List;

import static org.KeyCrafter.obj.KeyCrafterObject.KeyCrafter_ZSET;
import static org.KeyCrafter.util.ReplyUtil.*;

public class ZSetKey implements ZSetCommand {
    private static final String nanErr = "resulting score is not a number (NaN)";

    @Override
    public void zaddCommand(ClientSession c) {
        zaddGenericCommand(c,false);
    }

    @Override
    public void zincrbyCommand(ClientSession c) {
        zaddGenericCommand(c, true);
    }

    @Override
    public void zrevrangeCommand(ClientSession c) {
        zrangeGenericCommand(c,true);
    }

    @Override
    public void zrangeCommand(ClientSession c) {
        zrangeGenericCommand(c,false);
    }

    private static void zrangeGenericCommand(ClientSession c, boolean reverse) {
        String key = c.argv(1);
        boolean withScore = false;
        // 取出 start 和 end 参数
        long start, end;
        try {
            start = Long.parseLong(c.argv(2));
            end = Long.parseLong(c.argv(3));
        } catch (NumberFormatException e) {
            // 错误回复
            addReplyError(c,"value is not an integer or out of range");
            return;
        }
        // 确定是否显示分值
        if (c.argc() == 5 && c.argv(4).toLowerCase().equals("withscores")) {
            withScore = true;
        } else if (c.argc() > 5) {
            addReply(c, SharedReply.syntaxErr);
            return;
        }
        // 取出有序集合对象
        KeyCrafterObject zObj;
        if ((zObj = c.db().lookupKeyReadOrReply(c, key, SharedReply.emptyMultiBulk)) == null
                || !checkType(c, zObj, KeyCrafter_ZSET)) {
            return;
        }

        ZSetType zSetType = ((ZSetObj) zObj).getValue();
        // 将负数索引转换为正数索引
        int llen = zSetType.length();
        if (start < 0) {
            start = llen + start;
        }
        if (end < 0) {
            end = llen + end;
        }
        if (start < 0) {
            start = 0;
        }
        // 过滤/调整索引
        if (start > end || start >= llen) {
            addReply(c, SharedReply.emptyMultiBulk);
            return;
        }
        if (end >= llen) {
            end = llen - 1;
        }
        int rangeLen = (int) ((end - start) + 1);
        // 获得范围内的节点
        ZSetType.Node[] nodes = zSetType.getNodesByRange((int) start, rangeLen, reverse);
        // 回复
        List<String> reply = new ArrayList<>();
        for (ZSetType.Node node : nodes) {
            reply.add((String) node.member());
            if (withScore) {
                reply.add(String.valueOf(node.score()));
            }
        }
        addReplyArray(c,reply);
    }

    private static void zaddGenericCommand(ClientSession c, boolean incr) {
        String key = c.argv(1);
        int argc = c.argc();
        // 输入 score - member 成对出现
        if (argc % 2 != 0) {
            addReply(c, SharedReply.syntaxErr);
            return;
        }
        // 取出所有存入 score分值
        int elements = (argc - 2) / 2;
        double[] scores = new double[elements];
        int j;
        for (j = 0; j < elements; j++) {
            try {
                scores[j] = Double.parseDouble(c.argv(2+j*2));
            } catch (NumberFormatException e) {
                // 错误回复
                addReplyError(c,nanErr);
                return;
            }
        }
        // 取出有序集合对象
        KeyCrafterObject zObj = c.db().lookupKeyWrite(key);
        if (zObj == null) {
            // 有序集合不存在，创建新有序集合
            zObj = KeyCrafterObjFactory.newZSetObj();
            // 关联对象到数据库
            c.db().dbAdd(key,zObj);
        } else {
            // 对象存在，检查类型
            if (!(zObj instanceof ZSetObj)) {
                addReply(c,SharedReply.wrongTypeErr);
                return;
            }
        }

        ZSetType zsetType = ((ZSetObj) zObj).getValue();
        // 处理所有元素
        int added = 0; // 新增的元素数
        double score = 0;
        for (j = 0; j < elements; j++) {
            score = scores[j];
            // 查找成员
            String ele = c.argv(3+j*2);
            if (incr) {
                // ZINCRYBY 时执行
                // 增加分数
                score += zsetType.incr(ele,score);
            } else {
                // 添加或修改
                added += zsetType.add(score,ele);
            }
            c.db().incrDirty();
        }

        if (incr) { /* ZINCRBY */
            addReplyDouble(c,score);
        } else {  /* ZADD */
            addReplyLong(c,added);
        }
    }
}
